package cn.shanguoyu.base.springboot.starter.jwt.util;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.StrUtil;
import cn.shanguoyu.base.springboot.starter.cache.constant.CacheConstants;
import cn.shanguoyu.base.springboot.starter.jwt.CreateTokenParam;
import cn.shanguoyu.base.springboot.starter.jwt.JwtTokenProperties;
import cn.shanguoyu.base.springboot.starter.jwt.Token;
import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @description:TokenUtil
 * @author：sgy
 * @date: 2023-03-19
 */
@Component
public class TokenUtil {
    @Autowired
    private JwtTokenProperties tokenProperties;
    @Autowired
    private RedisTemplateProxy cache;

    /**
     * 构建token
     *
     * @param createTokenParam createTokenParam
     * @return TOKEN
     */
    public Token createToken(CreateTokenParam createTokenParam) {
        Token token = new Token();
        // 处理accessToken
        String accessToken = processAccessToken(createTokenParam);
        // 处理refreshToken
        String refreshToken = processRefreshToken(createTokenParam);
        // 构建返回值
        token.setAccessToken(accessToken);
        token.setRefreshToken(refreshToken);
        token.setAccessTokenExpireTime(SystemClock.now() + (long) tokenProperties.getExpireTime() * 60 * 1000);
        return token;
    }

    private String processRefreshToken(CreateTokenParam createTokenParam) {
        // 刷新token生成策略：如果是长时间有效的token（用于app），则默认15天有效期刷新token。如果是普通用户登录，则刷新token为普通token2倍数
        int expireTime = createTokenParam.isLongTerm() ? tokenProperties.getWxExpireTime() * 24 * 60 : tokenProperties.getRefreshTime();
        // 生成accessToken
        String refreshToken = createToken(createTokenParam, expireTime);
        // 存储key的前缀
        String refreshTokenKey = StrUtil.format(CacheConstants.JWT_REFRESH_TOKEN, refreshToken);
        // 存储refreshToken到Redis，key->jwt:access:refresh:{refreshToken}，value->1
        cache.put(refreshTokenKey, 1, expireTime, TimeUnit.MINUTES);
        return refreshToken;
    }

    private String processAccessToken(CreateTokenParam createTokenParam) {
        // 生成accessToken
        String accessToken = createToken(createTokenParam, tokenProperties.getExpireTime());
        // 存储key的前缀
        String accessTokenKey = StrUtil.format(CacheConstants.JWT_ACCESS_TOKEN, accessToken);
        // 存储accessToken到Redis，key->jwt:access:token:{accessToken}，value->1
        cache.put(accessTokenKey, "1", tokenProperties.getExpireTime(), TimeUnit.MINUTES);
        return accessToken;
    }


    /**
     * 刷新token
     *
     * @param oldRefreshToken 刷新token
     * @return token
     */
    public Token refreshToken(String oldRefreshToken) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(tokenProperties.getSecret())
                    .parseClaimsJws(oldRefreshToken).getBody();
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException |
                IllegalArgumentException e) {
        }

        //获取存储在claims中的自定义信息
        String customInfoJson = claims.get("customInfo").toString();
        CreateTokenParam createTokenParam = (CreateTokenParam) JSON.parse(customInfoJson);
        // 基础变量
        boolean longTerm = createTokenParam.isLongTerm();
        String refreshTokenKey = StrUtil.format(CacheConstants.JWT_REFRESH_TOKEN, oldRefreshToken);

        //如果缓存中有刷新token &&
        if (cache.hasKey(refreshTokenKey)) {
            Token token = new Token();
            // accessToken
            String accessToken = processAccessToken(createTokenParam);
            // refreshToken
            String refreshToken = processRefreshToken(createTokenParam);
            // 返回值处理
            token.setAccessToken(accessToken);
            token.setRefreshToken(refreshToken);
            token.setAccessTokenExpireTime(SystemClock.now() + (long) tokenProperties.getExpireTime() * 60 * 1000);
            // 清除缓存
            cache.delete(refreshTokenKey);
            return token;
        } else {
//            throw new ServiceException(ResultCode.USER_AUTH_EXPIRED);
            return null;
        }
    }

    /**
     * 生成token
     *
     * @param data           jwt主体对象
     * @param expirationTime 过期时间（分钟）
     * @return token字符串
     */
    private String createToken(Object data, int expirationTime) {
        //JWT 生成
        return Jwts.builder()
                // jwt 私有声明
                .claim("customInfo", JSON.toJSONString(data))
                // 失效时间 当前时间+过期分钟
                .setExpiration(new Date(SystemClock.now() + expirationTime * 60 * 1000))
                // 签名算法和密钥
                .signWith(SignatureAlgorithm.HS256, tokenProperties.getSecret())
                .compact();
    }
}
